Flutter ElevatedButton, TextButton, and OutlinedButton
Flutter provides several Material Design button widgets for handling user interactions. Among the most commonly used are ElevatedButton, TextButton, and OutlinedButton. Each button has a different visual style and is suitable for different types of actions.
These buttons are based on Flutter's ButtonStyle system, which allows developers to customize colors, padding, shape, elevation, text style, borders, sizes, and interaction states such as pressed, hovered, focused, and disabled.
1. What is a Button in Flutter?
A button is an interactive widget that allows the user to perform an action when tapped or activated.
Common button actions include:
- Submitting a form
- Opening another screen
- Saving data
- Deleting an item
- Canceling an operation
- Opening a dialog
- Performing calculations
- Calling an API
The basic structure of a Flutter button is:
ElevatedButton(
onPressed: () {
// Action
},
child: const Text('Click Me'),
)
The onPressed callback determines what happens when the button is activated. If both onPressed and onLongPress are null, the button becomes disabled.
2. Main Flutter Button Types
| Button |
Appearance |
Common Use |
| ElevatedButton |
Filled/elevated appearance |
Primary actions |
| TextButton |
Text without visible border or fill |
Low-emphasis and secondary actions |
| OutlinedButton |
Visible border with transparent/low-fill appearance |
Secondary actions and alternatives |
Flutter's Material Design button system also includes FilledButton and FilledButton.tonal, but this lesson focuses on ElevatedButton, TextButton, and OutlinedButton.
3. ElevatedButton
ElevatedButton is a Material Design button with a filled appearance and elevation. It is commonly used for important or primary actions in an application.
Basic ElevatedButton Example
ElevatedButton(
onPressed: () {
print('Button pressed');
},
child: const Text('Submit'),
)
How ElevatedButton Works
- onPressed: Defines the action performed when the button is pressed.
- child: Defines the button content.
- style: Allows customization of the button's appearance.
- onLongPress: Handles long-press interaction.
- onHover: Handles pointer hover events on supported platforms.
- onFocusChange: Detects focus changes.
ElevatedButton with Background and Text Color
ElevatedButton(
onPressed: () {
print('Login clicked');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
child: const Text('Login'),
)
ElevatedButton with Padding
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 15,
),
),
child: const Text('Continue'),
)
ElevatedButton with Rounded Corners
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Save'),
)
ElevatedButton with Elevation
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
elevation: 5,
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
),
child: const Text('Click Me'),
)
ElevatedButton with Icon
ElevatedButton.icon(
onPressed: () {
print('Download started');
},
icon: const Icon(Icons.download),
label: const Text('Download'),
)
ElevatedButton for Login
ElevatedButton(
onPressed: () {
print('Login successful');
},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
),
child: const Text('Login'),
)
When to Use ElevatedButton
- Login
- Register
- Submit
- Save
- Continue
- Checkout
- Confirm
- Download
4. TextButton
TextButton is a low-emphasis button that normally appears as text without a visible border or filled background. It is useful for secondary actions, toolbar actions, dialogs, and inline actions.
Basic TextButton Example
TextButton(
onPressed: () {
print('Cancel clicked');
},
child: const Text('Cancel'),
)
TextButton with Text Color
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
),
child: const Text('Learn More'),
)
TextButton with Background Color
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.blue,
),
child: const Text('Open'),
)
TextButton with Rounded Corners
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text('View Details'),
)
TextButton with Icon
TextButton.icon(
onPressed: () {
print('Edit clicked');
},
icon: const Icon(Icons.edit),
label: const Text('Edit'),
)
TextButton in a Dialog
AlertDialog(
title: const Text('Delete Item'),
content: const Text('Are you sure you want to delete this item?'),
actions: [
TextButton(
onPressed: () {
print('Cancelled');
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
print('Deleted');
},
child: const Text('Delete'),
),
],
)
When to Use TextButton
- Cancel
- Skip
- Learn More
- View Details
- Edit
- Forgot Password
- Dialog actions
- Toolbar actions
5. OutlinedButton
OutlinedButton is a button with a visible border and no prominent filled background by default. It is useful when an action should be noticeable but visually less prominent than a primary filled button.
Basic OutlinedButton Example
OutlinedButton(
onPressed: () {
print('Outlined button clicked');
},
child: const Text('Cancel'),
)
OutlinedButton with Border Color
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.blue,
width: 2,
),
),
child: const Text('View Profile'),
)
OutlinedButton with Rounded Corners
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
side: const BorderSide(
color: Colors.blue,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Register'),
)
OutlinedButton with Background Color
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.blue,
backgroundColor: Colors.blue.shade50,
side: const BorderSide(
color: Colors.blue,
),
),
child: const Text('Details'),
)
OutlinedButton with Icon
OutlinedButton.icon(
onPressed: () {
print('Upload clicked');
},
icon: const Icon(Icons.upload),
label: const Text('Upload'),
)
When to Use OutlinedButton
- Cancel
- Register
- View Details
- Choose Option
- Secondary navigation
- Alternative actions
- Upload
- Filter
6. Difference Between ElevatedButton, TextButton, and OutlinedButton
| Feature |
ElevatedButton |
TextButton |
OutlinedButton |
| Default Fill |
Filled/elevated appearance |
Transparent |
Generally transparent |
| Border |
Usually no visible border |
No visible border |
Visible border |
| Elevation |
Yes |
No prominent elevation |
No prominent elevation |
| Emphasis |
High |
Low |
Medium |
| Typical Use |
Primary action |
Secondary/inline action |
Secondary action |
| Example |
Submit |
Cancel |
View Details |
7. ButtonStyle
Flutter provides ButtonStyle for controlling the visual appearance of buttons. It can be used with ElevatedButton, TextButton, OutlinedButton, and other Material buttons.
Common ButtonStyle properties include:
- backgroundColor
- foregroundColor
- overlayColor
- elevation
- padding
- minimumSize
- fixedSize
- maximumSize
- side
- shape
- textStyle
- iconColor
- iconSize
ButtonStyle Example
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.blue),
foregroundColor: WidgetStatePropertyAll(Colors.white),
padding: WidgetStatePropertyAll(
EdgeInsets.symmetric(
horizontal: 25,
vertical: 15,
),
),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
child: const Text('Submit'),
)
For simple customization, styleFrom() is usually easier to read. For more detailed state-dependent styling, use ButtonStyle with widget-state properties.
8. Using styleFrom()
Flutter provides convenient styleFrom() methods for buttons. They allow developers to create a ButtonStyle using simple values.
ElevatedButton styleFrom
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: const Text('Save'),
)
TextButton styleFrom
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
),
child: const Text('Learn More'),
)
OutlinedButton styleFrom
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.blue,
side: const BorderSide(
color: Colors.blue,
width: 2,
),
),
child: const Text('View Profile'),
)
9. Button Width and Height
You can control button dimensions using properties such as minimumSize, fixedSize, and maximumSize.
Full Width Button
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50),
),
child: const Text('Login'),
)
Fixed Size Button
OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
fixedSize: const Size(200, 50),
),
child: const Text('Continue'),
)
10. Button Padding
Padding creates space between the button's content and its boundary.
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 16,
),
),
child: const Text('Submit'),
)
11. Button Shape
Buttons can be customized using different shapes.
Rounded Rectangle
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: const Text('Rounded'),
)
Pill-Shaped Button
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
),
child: const Text('Pill Button'),
)
12. Button with Icons
Flutter provides .icon constructors for several button types.
ElevatedButton with Icon
ElevatedButton.icon(
onPressed: () {},
icon: const Icon(Icons.save),
label: const Text('Save'),
)
TextButton with Icon
TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.edit),
label: const Text('Edit'),
)
OutlinedButton with Icon
OutlinedButton.icon(
onPressed: () {},
icon: const Icon(Icons.delete),
label: const Text('Delete'),
)
13. Disabled Buttons
A button becomes disabled when its action callback is null.
Disabled ElevatedButton
ElevatedButton(
onPressed: null,
child: const Text('Disabled'),
)
Disabled TextButton
TextButton(
onPressed: null,
child: const Text('Disabled'),
)
Disabled OutlinedButton
OutlinedButton(
onPressed: null,
child: const Text('Disabled'),
)
Disabled states are useful when an action should not be available until a required condition is satisfied.
14. Conditional Button State
Buttons can be enabled or disabled based on application state.
bool isFormValid = true;
ElevatedButton(
onPressed: isFormValid
? () {
print('Form submitted');
}
: null,
child: const Text('Submit'),
)
This approach is commonly used in forms where the submit button should only become active after valid input is provided.
15. Buttons in a Row
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {},
child: const Text('Save'),
),
const SizedBox(width: 10),
OutlinedButton(
onPressed: () {},
child: const Text('Cancel'),
),
],
)
16. Login Screen Example
import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
const TextField(
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 15),
const TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
print('Login clicked');
},
style: ElevatedButton.styleFrom(
minimumSize: const Size(
double.infinity,
50,
),
),
child: const Text('Login'),
),
TextButton(
onPressed: () {
print('Forgot password clicked');
},
child: const Text('Forgot Password?'),
),
OutlinedButton(
onPressed: () {
print('Create account clicked');
},
child: const Text('Create Account'),
),
],
),
),
);
}
}
17. Save, Cancel, and Delete Example
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
print('Cancel');
},
child: const Text('Cancel'),
),
OutlinedButton(
onPressed: () {
print('Delete');
},
child: const Text('Delete'),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {
print('Save');
},
child: const Text('Save'),
),
],
)
This arrangement demonstrates how different visual emphasis levels can be used for different actions.
18. Navigation Using Buttons
Buttons can be used to navigate between screens using Navigator.
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage(),
),
);
},
child: const Text('Open Next Page'),
)
Second Page
class SecondPage extends StatelessWidget {
const SecondPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Page'),
),
body: Center(
child: TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
),
),
);
}
}
19. Button Interaction States
Flutter buttons can have different visual states depending on user interaction.
| State |
Description |
| Enabled |
The button can be activated. |
| Disabled |
The button cannot be activated. |
| Pressed |
The user is currently pressing the button. |
| Hovered |
A pointer is positioned over the button. |
| Focused |
The button has keyboard or accessibility focus. |
| Selected |
Used when an application needs a selected interaction state. |
Flutter's widget-state properties can resolve different values depending on states such as pressed, hovered, focused, and disabled.
20. State-Dependent Button Styling
For advanced UI designs, ButtonStyle can use WidgetStateProperty to change styling according to the button's current state.
ElevatedButton(
onPressed: () {},
style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith(
(states) {
if (states.contains(WidgetState.pressed)) {
return Colors.green;
}
if (states.contains(WidgetState.hovered)) {
return Colors.blue;
}
return Colors.indigo;
},
),
),
child: const Text('Interactive Button'),
)
This allows developers to create buttons that respond visually to user interaction.
21. Global Button Theme
If the same button style should be used throughout an application, it is better to configure the application's theme instead of styling every button individually.
MaterialApp(
theme: ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
home: const HomePage(),
)
This approach helps maintain consistent button styling across the application.
22. Complete Example with All Three Buttons
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const ButtonDemo(),
);
}
}
class ButtonDemo extends StatelessWidget {
const ButtonDemo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Buttons'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
print('ElevatedButton pressed');
},
child: const Text('Elevated Button'),
),
const SizedBox(height: 15),
TextButton(
onPressed: () {
print('TextButton pressed');
},
child: const Text('Text Button'),
),
const SizedBox(height: 15),
OutlinedButton(
onPressed: () {
print('OutlinedButton pressed');
},
child: const Text('Outlined Button'),
),
],
),
),
);
}
}
23. Practical Comparison Example
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: () {
print('Primary action');
},
child: const Text('Submit Order'),
),
const SizedBox(height: 10),
OutlinedButton(
onPressed: () {
print('Secondary action');
},
child: const Text('Review Order'),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
print('Low priority action');
},
child: const Text('Cancel'),
),
],
)
In this example, the three buttons communicate different levels of visual emphasis.
24. Best Practices
- Use ElevatedButton for important primary actions.
- Use OutlinedButton for secondary actions that should still have a visible boundary.
- Use TextButton for low-emphasis actions and inline actions.
- Keep button labels short and action-oriented.
- Use icons when they improve understanding of the action.
- Maintain consistent button sizes throughout similar screens.
- Use theme-level styling for application-wide consistency.
- Make sure text has sufficient contrast against the button background.
- Do not place too many visually prominent buttons next to each other.
- Use disabled states when an action is temporarily unavailable.
- Provide meaningful labels for accessibility.
- Use appropriate spacing between adjacent buttons.
25. Common Mistakes
Mistake 1: Using Too Many Primary Buttons
Making every button an ElevatedButton can reduce the visual hierarchy of the interface.
Mistake 2: Very Long Button Labels
Keep button text concise and easy to understand.
Mistake 3: Poor Contrast
Make sure foreground and background colors provide sufficient readability.
Mistake 4: No Action Callback
ElevatedButton(
child: const Text('Submit'),
)
The button requires an onPressed callback. Use onPressed: null when you intentionally want a disabled button.
Mistake 5: Inconsistent Styling
Using different button shapes, sizes, and colors randomly can make an application look inconsistent. Prefer a reusable design system or application theme.
26. Quick Selection Guide
| Requirement |
Recommended Button |
| Primary action |
ElevatedButton |
| Secondary action with border |
OutlinedButton |
| Simple/low-emphasis action |
TextButton |
| Login |
ElevatedButton |
| Cancel in dialog |
TextButton |
| Register alternative |
OutlinedButton |
| Save |
ElevatedButton |
| View Details |
OutlinedButton or TextButton |
| Forgot Password |
TextButton |
27. Interview Questions
- What is ElevatedButton in Flutter?
- What is the difference between ElevatedButton and TextButton?
- What is the difference between TextButton and OutlinedButton?
- How do you change the background color of an ElevatedButton?
- How do you change the border of an OutlinedButton?
- How do you create a button with an icon?
- How do you disable a Flutter button?
- What is ButtonStyle?
- What is the purpose of styleFrom()?
- How can you create a full-width button?
- How can you create rounded buttons?
- How can button styling be controlled globally?
- What are widget interaction states?
- How can a button change its style when pressed or hovered?
- What happens when onPressed is null?
28. Practice Exercises
- Create a login screen using ElevatedButton, TextButton, and OutlinedButton.
- Create a registration form with a primary Submit button and secondary Cancel button.
- Create a product page with Buy Now, Add to Cart, and View Details buttons.
- Create a dialog containing Cancel and Delete actions.
- Create a full-width ElevatedButton with rounded corners.
- Create an OutlinedButton with a custom border color and width.
- Create a TextButton with an icon.
- Create disabled buttons and enable them conditionally.
- Create a global ElevatedButton theme using ThemeData.
- Create state-dependent button colors for hover and pressed states.
29. Key Takeaways
- ElevatedButton is suitable for prominent primary actions.
- TextButton is suitable for low-emphasis and inline actions.
- OutlinedButton is useful for secondary actions with a visible border.
- The onPressed callback handles button interaction.
- Setting onPressed to null disables the button.
- styleFrom() provides a convenient way to customize button styles.
- ButtonStyle provides detailed control over button appearance and states.
- Buttons can contain both text and icons.
- ThemeData can be used to create consistent application-wide button styles.
- Good button hierarchy improves usability and makes interfaces easier to understand.
30. Learning Resources
For structured Flutter training, visit:
Official Flutter Documentation